<!DOCTYPE html>

<html>

<head>
  <title>Example mirror</title>
  <script type="text/javascript" src="../three/build/three.js"></script>
  <script type="text/javascript" src="../three/examples/js/libs/stats.min.js"></script>
  <script type="text/javascript" src="../three/examples/js/objects/Reflector.js"></script>
  <script type="text/javascript" src="../three/examples/js/controls/OrbitControls.js"></script>


  <style>
    body {
      margin: 0;
      overflow: hidden;
    }
  </style>
</head>

<body>

  <div id="Stats-output"></div>
  <div id="WebGL-output"></div>

  <script type="text/javascript">
    let stats, controls;
    let camera, scene, renderer;
    let sphereGroup, smallSphere;

    let groundMirror, verticalMirror;

    function initScene() {
      scene = new THREE.Scene();
    }

    function initCamera() {
      camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
      camera.position.set(0, 190, 280)
      camera.lookAt(new THREE.Vector3(0, 0, 0));
    }

    // 创建胶囊体
    function createCapsule() {
      const radius = 4;
      const height = 18;
      // 存放样条曲线的点集
      const points = []

      //上半部分四分之一圆弧
      for (let i = Math.PI / 2; i > 0; i -= 0.1) {
        points.push(
          new THREE.Vector3(
            Math.cos(i) * radius,
            Math.sin(i) * radius + height / 2,
            0
          )
        )
      }

      //中间直线
      for (let i = height / 2; i > -height / 2; i -= 0.1) {
        points.push(
          new THREE.Vector3(
            radius,
            i,
            0
          )
        )
      }

      //下半部分四分之一圆弧
      for (let i = 0; i <= Math.PI / 2; i += 0.1) {
        points.push(
          new THREE.Vector3(
            Math.cos(i) * radius,
            -Math.sin(i) * radius - height / 2,
            0
          )
        )
      }

      // 补充一个点，去掉底部的小洞洞
      points.push(
        new THREE.Vector3(
          0,
          -radius - height / 2,
          0
        )
      )


      // 根据样条曲线创建扫描几何体
      const geom = new THREE.LatheGeometry(
        points,
        200,
        0,
        Math.PI * 2
      )


      // 创建材质
      const meshMaterial = new THREE.MeshPhongMaterial({
        side: THREE.DoubleSide,
        color: 0xfff000
      })
      const mesh = new THREE.Mesh(geom, meshMaterial)

      mesh.rotateX(-Math.PI * 0.8);
      mesh.rotateZ(-Math.PI * 0.2);
      mesh.position.y = 28;
      sphereGroup.add(mesh)
    }

    function createDoughnut() {
      const Radius = 10;
      const radius = 4;
      const height = 6;
      //绘制一个圆
      const curve = new THREE.EllipseCurve(
        0, 0, // ax, aY
        radius, radius, // xRadius, yRadius
        0, Math.PI * 2, // aStartAngle, aEndAngle
        false, // aClockwise
        0 // aRotation
      );

      const innerCirclePoints = curve.getPoints(100)


      // 存放样条曲线的点集
      const points = []
      innerCirclePoints.forEach(point => {
        point.x = point.x + Radius
        points.push(point)
      })



      //根据样条曲线创建扫描几何体
      const geom = new THREE.LatheGeometry(
        points,
        200,
        0,
        Math.PI * 2
      )


      // 创建材质
      const meshMaterial = new THREE.MeshPhongMaterial({
        color: 0xfe3221,
        side: THREE.DoubleSide
      })
      const mesh = new THREE.Mesh(geom, meshMaterial)

      mesh.position.x = 22;

      mesh.rotateX(-Math.PI * 0.8);
      mesh.rotateZ(-Math.PI * 0.2);
      mesh.position.y = 18;

      sphereGroup.add(mesh)
    }


    function createSmallSphere() {
      const geometry = new THREE.IcosahedronGeometry(5, 0);
      const material = new THREE.MeshPhongMaterial({
        color: 0xffffff,
        emissive: 0x333333,
        flatShading: true
      });
      smallSphere = new THREE.Mesh(geometry, material);
      scene.add(smallSphere);
    }

    function createWallAndLight() {
      const planeGeo = new THREE.PlaneGeometry(100.1, 100.1);

      //创建墙面
      const planeTop = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({
        color: 0xffffff
      }));
      planeTop.position.y = 100;
      planeTop.rotateX(Math.PI / 2);
      scene.add(planeTop);

      const planeBottom = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({
        color: 0xffffff
      }));
      planeBottom.rotateX(-Math.PI / 2);
      scene.add(planeBottom);

      const planeFront = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({
        color: 0x7f7fff
      }));
      planeFront.position.z = 50;
      planeFront.position.y = 50;
      planeFront.rotateY(Math.PI);
      scene.add(planeFront);

      const planeRight = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({
        color: 0x00ff00
      }));
      planeRight.position.x = 50;
      planeRight.position.y = 50;
      planeRight.rotateY(-Math.PI / 2);
      scene.add(planeRight);

      const planeLeft = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({
        color: 0xff0000
      }));
      planeLeft.position.x = -50;
      planeLeft.position.y = 50;
      planeLeft.rotateY(Math.PI / 2);
      scene.add(planeLeft);

      //灯光
      const mainLight = new THREE.PointLight(0xcccccc, 1.3, 250);
      mainLight.position.y = 60;
      scene.add(mainLight);

      const greenLight = new THREE.PointLight(0x00ff00, 0.25, 1000);
      greenLight.position.set(550, 50, 0);
      scene.add(greenLight);

      const redLight = new THREE.PointLight(0xff0000, 0.25, 1000);
      redLight.position.set(-550, 50, 0);
      scene.add(redLight);

      const blueLight = new THREE.PointLight(0x7f7fff, 0.25, 1000);
      blueLight.position.set(0, 50, 550);
      scene.add(blueLight);
    }

    function createMirrors() {

      //创建圆形水平镜面，用于将胶囊体、甜圈圈、多面体小球映射到地面上
      let geometry = new THREE.CircleGeometry(40, 64);
      groundMirror = new THREE.Reflector(geometry, {
        clipBias: 0.003,
        textureWidth: window.innerWidth * window.devicePixelRatio,
        textureHeight: window.innerHeight * window.devicePixelRatio,
        color: 0x777777
      });
      groundMirror.position.y = 0.5;
      groundMirror.rotateX(-Math.PI / 2);
      scene.add(groundMirror);

      //创建垂直镜面，用于将整个场景映射到Z轴负方向的空间中
      geometry = new THREE.PlaneGeometry(100, 100);
      verticalMirror = new THREE.Reflector(geometry, {
        clipBias: 0.003,
        textureWidth: window.innerWidth * window.devicePixelRatio,
        textureHeight: window.innerHeight * window.devicePixelRatio,
        color: 0x317fef
      });
      verticalMirror.position.y = 50;
      verticalMirror.position.z = -50;
      scene.add(verticalMirror);
    }

    function initModel() {

      //创建存放甜圈圈和胶囊体的组对象并添加到场景
      sphereGroup = new THREE.Object3D();
      scene.add(sphereGroup);

      createCapsule(); //创建胶囊体
      createDoughnut(); //创建甜圈圈
      createSmallSphere(); //创建多面体小球
      createWallAndLight(); //创建墙体和灯光
      createMirrors(); //创建水平镜面和垂直镜面
    }

    function initRender() {

      renderer = new THREE.WebGLRenderer({
        antialias: true,
        alpha: true
      })
      //renderer.shadowMap.enabled = true // 显示阴影
      renderer.setPixelRatio(window.devicePixelRatio);
      renderer.setSize(window.innerWidth, window.innerHeight);
      renderer.setClearColor(0x000000, 1) // 设置背景颜色
      document.getElementById("WebGL-output").appendChild(renderer.domElement);
    }

    //初始化轨道控制器
    function initControls() {
      clock = new THREE.Clock() // 创建THREE.Clock对象，用于计算上次调用经过的时间
      controls = new THREE.OrbitControls(camera, renderer.domElement)
      //controls.autoRotate = true // 是否自动旋转
    }

    function init() {
      initScene();
      initCamera();
      initRender();
      initStats();
      initControls();
      initModel();
      render();
    }

    function updateFun() {
      stats.update();
      const delta = clock.getDelta() // 获取自上次调用的时间差
      controls.update(delta) // 相机更新
      const timer = Date.now() * 0.01;

      //胶囊体和甜圈圈旋转更新
      sphereGroup.rotation.y -= 0.01;

      //更新多面体小球位置
      smallSphere.position.set(
        Math.cos(timer * 0.1) * 30,
        Math.abs(Math.cos(timer * 0.2)) * 20 + 5,
        Math.sin(timer * 0.1) * 30
      );
      smallSphere.rotation.y = (Math.PI / 2) - timer * 0.1;
      smallSphere.rotation.z = timer * 0.8;

    }

    function render() {
      updateFun()
      requestAnimationFrame(render);
      renderer.render(scene, camera);
    }

    function initStats() {
      stats = new Stats();
      stats.setMode(0); // 0: fps, 1: ms

      document.getElementById("Stats-output").appendChild(stats.domElement);
    }

    window.onload = init;
  </script>
</body>

</html>